﻿using System.Drawing;
using RaytracerLib.Layers;
using RaytracerLib.MathLib;
using TomanuExtensions;
using YAXLib;
using System.Diagnostics;
using RaytracerLib.Layers.Interpolators;
using System;
using RaytracerLib.Layers.OverlayCorrectors;
using RaytracerLib.Resamplers.Filters;

namespace RaytracerLib.Postprocessing
{
    public class TwistEffect : Postprocessor
    {
        private const int SUPERSAMPLING = 3;
        private const FilterType FILTER_DOWN = FilterType.Bicubic;
        private const FilterType FILTER_UP = FilterType.Box;
        private const InterpolationMethod INTERPOLATION = InterpolationMethod.NearestNeighbour;
        private const double POWER_FACTOR = 3;

        [YAXNode]
        public double Angle = 180;

        [YAXNode]
        public double CenterX = 0.5;

        [YAXNode]
        public double CenterY = 0.5;

        [YAXNode]
        public double RadiusX = 0.5;

        [YAXNode]
        public double RadiusY = 0.5;

        internal override void PostprocessFinalize(ColorArrayFloat a_ar)
        {
            ColorArrayFloat src = new ColorArrayFloat(a_ar);
            src.OverlayMethod = OverlayMethod.Edge;
            src.InterpolationMethod = INTERPOLATION;
            ColorArrayFloat dest = new ColorArrayFloat(src.Width * SUPERSAMPLING, src.Height * SUPERSAMPLING);
            src.Resize(dest, FilterType.Box);
            
            double angle = MathExtensions.ToRad(Angle);
            double cx = CenterX * dest.Width;
            double cy = CenterY * dest.Height;
            double rx = RadiusX * dest.Width;
            double ry = RadiusY * dest.Height;
            double radius_ratio = rx / ry;
            double rmax = rx;

            for (int destx = 0; destx < dest.Width; destx++)
            {
                for (int desty = 0; desty < dest.Height; desty++)
                {
                    double dx = destx - cx;
                    double dy = desty - cy;

                    dy *= radius_ratio;

                    double r = MathExtensions.Hypot(dx, dy);

                    if (r > rmax)
                        continue;

                    double shift = Math.Pow(1 - r / rmax, POWER_FACTOR);
                    double theta = Math.Atan2(dy, dx) + shift * angle;
                    double srcx = r * Math.Cos(theta);
                    double srcy = r * Math.Sin(theta);

                    srcy /= radius_ratio;

                    srcx += cx;
                    srcy += cy;

                    srcx /= SUPERSAMPLING;
                    srcy /= SUPERSAMPLING;

                    if (srcx < 0)
                        continue;
                    if (srcx >= src.Width)
                        continue;
                    if (srcy < 0)
                        continue;
                    if (srcy >= src.Height)
                        continue;

                    dest[destx, desty] = src.GetColor(srcx, srcy);
                }
            }

            dest.Resize(a_ar, FILTER_DOWN);
        }
    }
}